#include<iostream>
#include<vector>
#include <errno.h>
#include <cstring>
#include <string>
#include <cstdio>
#include <cassert>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;

typedef void(*func)();

void Logfunc()
{
    cout<<"正在执行日志任务!"<<endl;
}

void MySQLfunc()
{
    cout<<"正在执行数据库任务!"<<endl;
}

void Interfunc()
{
    cout<<"正在执行网络任务!"<<endl;
}


//任务函数对象
class Func
{
public:
    Func()
    {
        //向数组中写入函数
        f.push_back(Logfunc);
        f.push_back(MySQLfunc);
        f.push_back(Interfunc);
    }

    //仿函数调用
    void operator()(int command)
    {
        if(command >= 0 && command < f.size()) f[command]();
    }

    std::vector<func> f;
};


//进程对象
class Task 
{
public:
    Task(pid_t id,int fd)
        :_id(id)
        ,_writefd(fd)
    {}
    ~Task(){}
public:
    pid_t _id; //进程pid
    int _writefd; //进程通信管道写fd
};

//子进程执行流
void WaitCommand()
{
    Func func; //创建任务对象
    while(true)
    {
        int command = 0;
        int n = read(0,&command,4);//读取四字节指令
        if(n == -1) //读取出错也退出
        {
            cerr<<"read error"<<endl;
            break;
        }
        else if(n == sizeof(int))
        {
            cout<<"进程"<<getpid()<<"执行任务:";//显示执行的进程pid
            func(command); //仿函数调用任务
        }
        else break; //n==0直接退出
    }
}


void CreatTask(vector<Task>& t,int TaskNum)
{
    if(TaskNum>64) TaskNum = 64; //禁止创建过多进程
    vector<int> nfd; //存储父进程已获取的管道写文件描述符
    for(int i = 0;i<TaskNum;++i)
    {
        //创建子进程前 先构建专属管道
        int fd[2] = {0};
        int n = pipe(fd);
        assert(n != -1);

        //创建子进程
        pid_t id = fork();
        assert(id != -1);
        if(id == 0) //子进程
        {
            //关闭冗余的fd
            for(auto& d:nfd) close(d); 
            //关闭写端
            close(fd[1]); 
            //将写文件描述符重定向读取端到标准输出
            dup2(fd[0],0); 
            //执行业务函数
            WaitCommand();
            //关闭读文件描述符
            close(fd[0]);
            exit(0);//退出
        }

        //父进程
        close(fd[0]); //关闭读
        t.push_back(Task(id,fd[1])); //写入进程对象 记录该子进程
        nfd.push_back(fd[1]); //写入父进程冗余的fd防止被其他子进程继承
    }
}

int menu()
{
    cout<<"**********************************"<<endl;
    cout<<"*0.执行日志任务  1.执行数据库任务*"<<endl;
    cout<<"*2.执行网络任务  3.退出          *"<<endl;
    cout<<"**********************************"<<endl;
    cout<<"请选择业务> ";
    int input = 0;
    cin >> input;
    cout<<endl;
    return input;

}

//关闭和回收子进程
void clean(const vector<Task>& t)
{
    for(const auto& n:t)
    {
        close(n._writefd); //关闭写端
        cout<<"子进程"<<n._id<<"退出"<<endl;
        waitpid(n._id,NULL,0);
        cout<<"子进程"<<n._id<<"被回收"<<endl;
    }
}

//任务派发
void ExecProc(const vector<Task>& t)
{
    int num = 0;
    //分配任务
    while(true)
    {
        //选择任务
        int command = menu(); 
        //如果任务号不正确 则不处理
        if(command < 0 && command > 3) {continue;}
        if(command == 3) break;
        write(t[num]._writefd,&command,sizeof(int));
        ++num;
        num %= 3;
        usleep(500);
    }
}

int main()
{
    //实现父进程与多个子课程进行通信
    vector<Task> t;

    //创建TaskNum个进程池
    CreatTask(t,3); 

    //派发任务
    ExecProc(t); 

    //执行清理任务
    clean(t); 
    return 0;
}

